// test_suite_sdv_eal_sm4.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

#include "crypt_eal_cipher.h"  // CRYPT_EAL_Cipher*
#include "crypt_algid.h"       // CRYPT_CIPHER_SM4_CBC
#include "crypt_types.h"       // CRYPT_PADDING_PKCS7


#ifndef ASSERT_TRUE
#define ASSERT_TRUE(cond) do { \
    if (!(cond)) { \
        fprintf(stderr, "[ASSERT_TRUE] %s:%d  (%s)\n", __FILE__, __LINE__, #cond); \
        exit(EXIT_FAILURE); \
    } \
} while (0)
#endif

#ifndef ASSERT_EQ
#define ASSERT_EQ(a,b) ASSERT_TRUE((a)==(b))
#endif


static int hexval(char c) {
    if (c>='0' && c<='9') return c-'0';
    if (c>='a' && c<='f') return c-'a'+10;
    if (c>='A' && c<='F') return c-'A'+10;
    return -1;
}
static uint8_t* hex2bin(const char* hex, size_t* outLen) {
    size_t n = strlen(hex);
    // 跳过前缀 0x/0X
    if (n>=2 && hex[0]=='0' && (hex[1]=='x' || hex[1]=='X')) { hex+=2; n-=2; }
    // 去掉空白
    char* buf = (char*)malloc(n+1);
    size_t m=0;
    for (size_t i=0;i<n;i++) {
        if (hex[i]==' '||hex[i]=='\t'||hex[i]=='\n'||hex[i]=='\r') continue;
        buf[m++]=hex[i];
    }
    buf[m]='\0';
    if (m%2!=0) { free(buf); return NULL; }

    size_t bytes = m/2;
    uint8_t* out = (uint8_t*)malloc(bytes);
    for (size_t i=0;i<bytes;i++) {
        int hi=hexval(buf[2*i]), lo=hexval(buf[2*i+1]);
        if (hi<0 || lo<0) { free(out); free(buf); return NULL; }
        out[i]=(uint8_t)((hi<<4)|lo);
    }
    free(buf);
    *outLen = bytes;
    return out;
}


typedef struct {
    uint8_t* key; size_t keyLen;
    uint8_t* iv;  size_t ivLen;
    uint8_t* pt;  size_t ptLen;
    uint8_t* ct;  size_t ctLen;  // 可选期望密文
} TestCase;

static void free_case(TestCase* tc) {
    if (!tc) return;
    if (tc->key) { memset(tc->key, 0, tc->keyLen); free(tc->key); }
    if (tc->iv)  { memset(tc->iv,  0, tc->ivLen ); free(tc->iv ); }
    if (tc->pt)  { free(tc->pt); }
    if (tc->ct)  { free(tc->ct); }
    memset(tc, 0, sizeof(*tc));
}

static int load_cases(const char* path, TestCase** out, size_t* outCnt) {
    FILE* f = fopen(path, "r");
    if (!f) return -1;

    char line[4096];
    TestCase cur = {0};
    size_t cap=8, cnt=0;
    TestCase* arr = (TestCase*)calloc(cap, sizeof(TestCase));

    auto flush_case = [&](void){
        if (cur.key && cur.iv && cur.pt) {
            if (cnt==cap) { cap*=2; arr = (TestCase*)realloc(arr, cap*sizeof(TestCase)); }
            arr[cnt++] = cur;
            memset(&cur, 0, sizeof(cur)); // 注意：这里是浅清零，已转移所有权
        } else {
            // 清理未完整的
            free_case(&cur);
        }
    };

    while (fgets(line, sizeof(line), f)) {
        // 去注释/空行
        if (line[0]=='#' || line[0]==';' || line[0]=='\n' || line[0]=='\r') continue;
        if (strncmp(line, "[case]", 6)==0) { flush_case(); continue; }

        char *eq = strchr(line, '=');
        if (!eq) continue;
        *eq = '\0';
        char *k = line, *v = eq+1;
        // 去尾换行
        char* nl = strpbrk(v, "\r\n"); if (nl) *nl = '\0';

        if (strcmp(k, "key")==0) {
            if (cur.key) { free(cur.key); cur.key=NULL; }
            cur.key = hex2bin(v, &cur.keyLen);
        } else if (strcmp(k, "iv")==0) {
            if (cur.iv) { free(cur.iv); cur.iv=NULL; }
            cur.iv = hex2bin(v, &cur.ivLen);
        } else if (strcmp(k, "pt")==0) {
            if (cur.pt) { free(cur.pt); cur.pt=NULL; }
            cur.pt = hex2bin(v, &cur.ptLen);
        } else if (strcmp(k, "ct")==0) {
            if (cur.ct) { free(cur.ct); cur.ct=NULL; }
            cur.ct = hex2bin(v, &cur.ctLen);
        }
    }

    flush_case();
    fclose(f);

    *out = arr; *outCnt = cnt;
    return 0;
}


static int run_one(const TestCase* t, size_t index) {
    ASSERT_TRUE(t && t->key && t->iv && t->pt);
    ASSERT_EQ(t->keyLen, 16);   // SM4 固定 128-bit key
    ASSERT_EQ(t->ivLen,  16);   // CBC 需要 128-bit IV（等于分组长度）

    CRYPT_EAL_CipherCtx* ctx = CRYPT_EAL_CipherNewCtx(CRYPT_CIPHER_SM4_CBC);
    ASSERT_TRUE(ctx != NULL);

    int32_t ret = CRYPT_EAL_CipherSetPadding(ctx, CRYPT_PADDING_PKCS7);
    ASSERT_EQ(ret, 0);

    // --- Encrypt ---
    ret = CRYPT_EAL_CipherInit(ctx, t->key, (uint32_t)t->keyLen,
                                    t->iv,  (uint32_t)t->ivLen, true);
    ASSERT_EQ(ret, 0);

    uint32_t capEnc = (uint32_t)(t->ptLen + 16); // PKCS7 至多 +1 block
    uint8_t* enc = (uint8_t*)malloc(capEnc);
    ASSERT_TRUE(enc != NULL);

    uint32_t n1=0, n2=0;
    ret = CRYPT_EAL_CipherUpdate(ctx, t->pt, (uint32_t)t->ptLen, enc, &n1);
    ASSERT_EQ(ret, 0);
    ret = CRYPT_EAL_CipherFinal(ctx, enc + n1, &n2);
    ASSERT_EQ(ret, 0);
    uint32_t encLen = n1 + n2;

    // 如果 .data 给了期望密文，校验之
    if (t->ct) {
        ASSERT_EQ(encLen, (uint32_t)t->ctLen);
        ASSERT_TRUE(memcmp(enc, t->ct, encLen) == 0);
    }

    // --- Decrypt ---
    CRYPT_EAL_CipherDeinit(ctx);
    ret = CRYPT_EAL_CipherSetPadding(ctx, CRYPT_PADDING_PKCS7);
    ASSERT_EQ(ret, 0);
    ret = CRYPT_EAL_CipherInit(ctx, t->key, (uint32_t)t->keyLen,
                                    t->iv,  (uint32_t)t->ivLen, false);
    ASSERT_EQ(ret, 0);

    uint8_t* dec = (uint8_t*)malloc(encLen);
    ASSERT_TRUE(dec != NULL);
    uint32_t d1=0, d2=0;
    ret = CRYPT_EAL_CipherUpdate(ctx, enc, encLen, dec, &d1);
    ASSERT_EQ(ret, 0);
    ret = CRYPT_EAL_CipherFinal(ctx, dec + d1, &d2);
    ASSERT_EQ(ret, 0);
    uint32_t decLen = d1 + d2;

    ASSERT_EQ(decLen, (uint32_t)t->ptLen);
    ASSERT_TRUE(memcmp(dec, t->pt, decLen) == 0);

    // 清理
    memset(enc, 0, encLen); free(enc);
    memset(dec, 0, decLen); free(dec);
    CRYPT_EAL_CipherDeinit(ctx);
    CRYPT_EAL_CipherFreeCtx(ctx);

    printf("case %zu: PASS\n", index+1);
    return 0;
}

int main(int argc, char** argv) {
    const char* dataPath = (argc>=2) ? argv[1] : "test_suite_sdv_eal_sm4.data";

    TestCase* cases = NULL;
    size_t cnt = 0;
    if (load_cases(dataPath, &cases, &cnt) != 0) {
        fprintf(stderr, "Failed to open data file: %s\n", dataPath);
        return EXIT_FAILURE;
    }
    ASSERT_TRUE(cnt > 0);

    size_t passed = 0;
    for (size_t i=0;i<cnt;i++) {
        if (run_one(&cases[i], i)==0) passed++;
        free_case(&cases[i]);
    }
    free(cases);

    printf("Run %zu testcases, passed: %zu, failed: %zu\n", cnt, passed, cnt-passed);
    return (passed==cnt) ? EXIT_SUCCESS : EXIT_FAILURE;
}
